package my.tree;

import java.util.Comparator;

/**
 * <p>
 * 二叉搜索树
 * </p>
 *
 * @author AJun
 * @since 2020年8月20日
 */
@SuppressWarnings("ALL")
public class BST<E> extends BinaryTree<E> {

    /**
     * 元素比较器
     */
    private final Comparator<E> comparator;

    /**
     * 空参构造
     * <p>
     * 没有传入比较器，则存储的元素必须 implements Comparable 接口实现比较逻辑，否则会报异常
     * </p>
     */
    public BST() {
        this(null);
    }

    /**
     * 有参构造
     *
     * @param comparator 用于元素比较的比较器
     */
    public BST(Comparator<E> comparator) {
        this.comparator = comparator;
    }

    /**
     * 判断二叉搜索树中是否包含与传入元素相同的节点
     *
     * @param element 要查找的元素
     * @return true: 包含; false: 不包含
     */
    public boolean contains(E element) {
        return node(element) != null;
    }

    /**
     * 给二叉搜索树添加元素
     *
     * @param element 要添加的元素
     */
    public void add(E element) {
        elementNotNullCheck(element);

        // 添加的第一个节点，根节点，根节点没有父节点
        if (root == null) {
            root = createNode(element, null);
            size++;

            // 添加之后对平衡的调整
            afterAdd(root);
            return;
        }

        // 找到要添加元素的父节点
        Node<E> parent = root;
        Node<E> node = root;
        int cmp = 0;
        while (node != null) {
            cmp = compare(element, node.element);
            parent = node;

            if (cmp > 0) {
                node = node.right;
            } else if (cmp < 0) {
                node = node.left;
            } else {
                // cmp == 0
                // 相等则覆盖其值，防止自定义对象的其它属性值不一样
                node.element = element;
                return;
            }
        }

        // 找到要插入到父节点的左还是右
        Node<E> newNode = createNode(element, parent);
        if (cmp > 0) {
            parent.right = newNode;
        } else {
            parent.left = newNode;
        }

        size++;
        // 添加之后对平衡的调整
        afterAdd(newNode);
    }

    /**
     * 删除二叉搜索树中与给定元素相同的节点
     *
     * @param element 要删除的元素
     */
    public void remove(E element) {
        remove(node(element));
    }

    /**
     * 删除指定的节点
     *
     * @param node 要删除的节点
     */
    private void remove(Node<E> node) {
        if (node == null) {
            return;
        }

        size--;

        // 度为 2 的节点，用其后继节点的元素覆盖此节点的元素，然后将此后继节点删除
        if (node.hasTwoChildren()) {
            // 找到此节点的后继节点
            Node<E> s = successor(node);
            node.element = s.element;
            node = s;
        }

        // 删除 node 节点(node 的度必为 1 或 0)
        // replacement 是 node 的子节点
        Node<E> replacement = (node.left != null) ? node.left : node.right;
        if (replacement != null) {
            // node 是度为 1 的节点
            // 更改 parent
            replacement.parent = node.parent;
            // 更改 parent 的 left 和 right 的指向
            if (node.parent == null) {
                // node 是度为 1 的节点并且是根节点
                root = replacement;
            } else {
                if (node == node.parent.left) {
                    node.parent.left = replacement;
                } else {
                    node.parent.right = replacement;
                }
            }

            // 删除后调整，恢复平衡
            afterRemove(replacement);
        } else if (node.parent == null) {
            // node 是叶子节点 且 node 是根节点
            root = null;

            // 删除后调整，恢复平衡
            afterRemove(node);
        } else {
            // node 是叶子节点，但不是根节点
            if (node == node.parent.left) {
                // 如果 node 是其父节点的左子节点，则删除父节点的左子节点
                node.parent.left = null;
            } else {
                // 如果 node 是其父节点的右子节点，则删除父节点的右子节点
                node.parent.right = null;
            }

            // 删除后调整，恢复平衡
            afterRemove(node);
        }
    }

    /**
     * 平衡树: 子类实现添加node后的平衡调整
     *
     * @param node 添加的节点
     */
    protected void afterAdd(Node<E> node) {
    }

    /**
     * 平衡树: 子类实现删除node后的平衡调整
     *
     * @param node 被删除的节点 或者 用于取代被删除节点的子节点(当被删除节点度为1时)
     */
    protected void afterRemove(Node<E> node) {
    }

    /**
     * 获取二叉搜索树中与传入元素相同的节点
     *
     * @param element 要查找的元素
     * @return 与此元素相同的节点
     */
    private Node<E> node(E element) {
        Node<E> node = root;
        while (node != null) {
            int cmp = compare(element, node.element);
            if (cmp == 0) {
                return node;
            } else if (cmp > 0) {
                node = node.right;
            } else {
                // cmp < 0
                node = node.left;
            }
        }
        return null;
    }

    /**
     * 比较 e1 与 e2 并返回比较结果
     *
     * @param e1 要比较的第一个元素
     * @param e2 要比较的第二个元素
     * @return 0:e1=e2; 1:e1>e2; -1:e1<e2
     */
    private int compare(E e1, E e2) {
        if (comparator != null)
            return comparator.compare(e1, e2);

        // 判断传入的类是否实现了 Comparable
        if (e1 instanceof Comparable) {
            return ((Comparable<E>) e1).compareTo(e2);
        }

        // 没有传入比较器，抛出类型转换异常
        throw new ClassCastException("Element must implement Comparable interface");
    }

    /**
     * 空值检测，二叉搜索树节点存储的元素不得为 null
     *
     * @param element 节点存储的元素
     */
    private void elementNotNullCheck(E element) {
        if (element == null) {
            throw new IllegalArgumentException("Element must not be null");
        }
    }

}
